package work.xiaohong.search.controller;

import com.alibaba.fastjson.JSON;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.support.replication.ReplicationResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import work.xiaohong.search.config.ElasticsearchConfig;
import work.xiaohong.search.entity.Result;
import work.xiaohong.search.entity.StatusCode;

import java.util.List;
import java.util.Map;

@RequestMapping("/document")
@RestController
public class DocumentController {

    @Autowired
    private IndexController indexController;

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    /**
     * 判断资源是否存在
     * @param index
     * @param id
     * @return
     * @throws Exception
     */
    @GetMapping("/exists/{index}/{id}")
    private Result<Boolean> exists(@PathVariable(name = "index") String index, @PathVariable(name = "id") String id) throws Exception {
        if (!indexController.exists(index).getData()){
            return new Result(false, StatusCode.ERROR,"索引不存在");
        }
        GetRequest getRequest = new GetRequest(index, id);
        getRequest.fetchSourceContext(new FetchSourceContext(false)); //禁用抓取_source
        getRequest.storedFields("_none_"); // 禁用获取存储的字段
        boolean exists = restHighLevelClient.exists(getRequest, ElasticsearchConfig.COMMON_OPTIONS);
        return new Result<Boolean>(true, StatusCode.OK,"查询成功",exists);
    }

    /**
     * 根据id获取文档
     * @param index
     * @param id
     * @return
     * @throws Exception
     */
    @GetMapping("/{index}/{id}")
    private Result findById(@PathVariable(name = "index") String index, @PathVariable(name = "id") String id) throws Exception {
        if (!indexController.exists(index).getData()){
            return new Result(false, StatusCode.ERROR,"索引不存在");
        }
        GetRequest getRequest = new GetRequest(index, id);
        GetResponse getResponse = restHighLevelClient.get(getRequest, ElasticsearchConfig.COMMON_OPTIONS);
        if (getResponse.isExists()) {
            long version = getResponse.getVersion();
            String sourceAsString = getResponse.getSourceAsString();
            Map<String, Object> sourceAsMap = getResponse.getSourceAsMap();
            byte[] sourceAsBytes = getResponse.getSourceAsBytes();
            return new Result<Boolean>(true, StatusCode.OK,"查询成功",sourceAsMap);
        } else {
            return new Result<Boolean>(false, StatusCode.ERROR,"文档不存在");
        }
    }

    /**
     * 根据id删除文档
     * @param index
     * @param id
     * @return
     * @throws Exception
     */
    @DeleteMapping("/{index}/{id}")
    private Result deleteById(@PathVariable(name = "index") String index, @PathVariable(name = "id") String id) throws Exception {
        if (!indexController.exists(index).getData()){
            return new Result(false, StatusCode.ERROR,"索引不存在");
        }

        DeleteRequest request = new DeleteRequest(index,id);
        DeleteResponse deleteResponse = restHighLevelClient.delete(request, ElasticsearchConfig.COMMON_OPTIONS);
        ReplicationResponse.ShardInfo shardInfo = deleteResponse.getShardInfo();

//        if (shardInfo.getTotal() != shardInfo.getSuccessful()) {
//            return new Result<Boolean>(false, StatusCode.ERROR,"处理成功分片数小于总分片数");
//        }

        if (shardInfo.getFailed() > 0) {
            for (ReplicationResponse.ShardInfo.Failure failure :
                    shardInfo.getFailures()) {
                String reason = failure.reason();
            }
            return new Result<Boolean>(false, StatusCode.ERROR,"存在潜在威胁");
        }


        if (deleteResponse.getResult() == DocWriteResponse.Result.NOT_FOUND) {
            return new Result<Boolean>(false, StatusCode.ERROR,"文档不存在");
        }else {
            return new Result<Boolean>(true, StatusCode.OK,"文档删除成功");
        }

    }


    /**
     * 根据id更新文档
     * @param index
     * @return
     * @throws Exception
     */
    @PutMapping("/{index}")
    private Result updateById(@PathVariable(name = "index") String index, @RequestBody Map map) throws Exception {
        if (!indexController.exists(index).getData()){
            return new Result(false, StatusCode.ERROR,"索引不存在");
        }

        Object id = map.get("id");
        if (id == null){
            return new Result(false, StatusCode.ERROR,"未传递id字段");
        }

        if(!(id instanceof String)){
            return new Result(false, StatusCode.ERROR,"请传递字符串类型的id");
        }

        UpdateRequest request  = new UpdateRequest(index,id.toString());

        request.doc(map);
        try {
            UpdateResponse updateResponse = restHighLevelClient.update(request, ElasticsearchConfig.COMMON_OPTIONS);
            if (updateResponse.getResult() == DocWriteResponse.Result.CREATED) {
                // 处理第一次创建文档的情况（upsert）
                return new Result<Boolean>(true, StatusCode.OK,"文档创建成功");
            } else if (updateResponse.getResult() == DocWriteResponse.Result.UPDATED) {
                // 处理文档更新的情况
                return new Result<Boolean>(true, StatusCode.OK,"文档更新成功");
            } else if (updateResponse.getResult() == DocWriteResponse.Result.DELETED) {
                // 处理文档被删除的情况
                return new Result<Boolean>(true, StatusCode.OK,"文档删除成功");
            } else if (updateResponse.getResult() == DocWriteResponse.Result.NOOP) {
                // 处理文档没有受到更新影响的情况，即没有对文档执行任何操作（noop）
                return new Result<Boolean>(true, StatusCode.OK,"无操作");
            }else {
                return new Result(false, StatusCode.ERROR,"灵异事件");
            }
        } catch (ElasticsearchException e) {
            if (e.status() == RestStatus.NOT_FOUND) {
                return new Result<Boolean>(false, StatusCode.ERROR,"文档不存在");
            }else {
                return new Result<Boolean>(false, StatusCode.ERROR,"出现未知错误");
            }
        }
    }

    /**
     * 向指定索引插入 单条文档
     * @param index
     * @param map
     * @return
     * @throws Exception
     */
    @PostMapping("/{index}")
    private Result insertOne(@PathVariable(name = "index") String index, @RequestBody Map map) throws Exception {

        if (!indexController.exists(index).getData()){
            return new Result(false, StatusCode.ERROR,"索引不存在");
        }

        IndexRequest request = new IndexRequest(index);
        Object id = map.get("id");
        if (id == null){
            return new Result(false, StatusCode.ERROR,"未携带id");
        }

        if (exists(index,id.toString()).getData()){
            return new Result(false, StatusCode.ERROR,"文档已存在");
        }

        if(!(id instanceof String)){
            return new Result(false, StatusCode.ERROR,"请传递字符串类型的id");
        }

        request.id(id.toString());

        String jsonString = JSON.toJSONString(map);
        request.source(jsonString, XContentType.JSON);

        IndexResponse response = restHighLevelClient.index(request, ElasticsearchConfig.COMMON_OPTIONS);
        ReplicationResponse.ShardInfo shardInfo = response.getShardInfo();

//        if (shardInfo.getTotal() != shardInfo.getSuccessful()) {
//            //处理成功分片数小于总分片数的情况
//            return new Result(false, StatusCode.ERROR,"处理成功分片数小于总分片数");
//        }

        if (shardInfo.getFailed() > 0) {
            for (ReplicationResponse.ShardInfo.Failure failure :
                    shardInfo.getFailures()) {
                // 处理潜在的故障
                String reason = failure.reason();
            }
            return new Result(false, StatusCode.ERROR,"出现以外故障");
        }
        if (response.getResult() == DocWriteResponse.Result.CREATED) {
            //处理（如果需要）第一次创建文档的情况
            return new Result(false, StatusCode.OK,"创建文档成功");
        }else if (response.getResult() == DocWriteResponse.Result.UPDATED) {
            //处理（如果需要）文档被重写的情况，因为它已经存在
            return new Result(false, StatusCode.OK,"更新文档成功");
        }else {
            return new Result(false, StatusCode.ERROR,"灵异事件");
        }

    }


    /**
     * 向指定索引批量删除文档
     * @param index
     * @param ids
     * @return
     * @throws Exception
     */
    @PostMapping("/bulk/{index}")
    private Result deleteBulk(@PathVariable(name = "index") String index, @RequestBody List<String> ids) throws Exception {

        BulkRequest request = new BulkRequest();
        for (String id : ids) {
            request.add(new DeleteRequest(index, id));
        }
        BulkResponse bulkResponse = restHighLevelClient.bulk(request, ElasticsearchConfig.COMMON_OPTIONS);
        if (bulkResponse.hasFailures()) {
            return new Result(false, StatusCode.ERROR,"批量删除失败");
        }else {
            return new Result(false, StatusCode.OK,"批量删除成功");
        }

    }


}
